import type { InterfaceHandler } from './handler/core'

import type {
  BusinessParams,
  PrivateCustomEventName,
  PublicCustomEventName,
  SectionDelayParams,
  SystemConfig,
} from './types'

import { LatRequest, ProcessorAudio, RecordActuator, SplitAudio } from './process'
import { SystemStatus } from './types'
import { createEventBus } from './utils/event-bus'

class LatController {
  private recordActuatorInstance: RecordActuator = new RecordActuator()

  private processorAudioInstance: ProcessorAudio = new ProcessorAudio()

  private splitAudioInstance: SplitAudio = new SplitAudio()

  private latRequestInstance: LatRequest = new LatRequest()

  private systemStatus: SystemStatus = SystemStatus.OFFLINE

  private isWatchBrowserVolumeChange: boolean = false

  public $bus = createEventBus<PublicCustomEventName | PrivateCustomEventName>()

  constructor(
    systemConfig: Required<SystemConfig>,
    businessParams: Required<BusinessParams>,
    sectionDelayParams: Required<SectionDelayParams>,
  ) {
    this.latRequestInstance.initProperty(systemConfig, businessParams, sectionDelayParams)

    this.recordActuatorInstance.setExecuteController(this)
    this.processorAudioInstance.setExecuteController(this)
    this.splitAudioInstance.setExecuteController(this)
    this.latRequestInstance.setExecuteController(this)

    this.recordActuatorInstance.linkHandler(this.processorAudioInstance as unknown as InterfaceHandler)
    this.processorAudioInstance.linkHandler(this.splitAudioInstance as unknown as InterfaceHandler)
    this.splitAudioInstance.linkHandler(this.latRequestInstance as unknown as InterfaceHandler)

    this.bindEvent()
  }

  private bindEvent(): void {
    this.$bus.on('_appFinish', () => {
      this.finish()

      if (this.isWatchBrowserVolumeChange) {
        this.watch()
      }
    })

    /**
     * 识别结果
     */
    this.$bus.on('_appResultText', (event) => {
      this.emit('appResultText', event)
    })

    this.$bus.on('_appResponseText', (event) => {
      this.emit('appResponseText', event)
    })
  }

  /**
   * 监听浏览器音频输入，当音量大于20%且系统离线时自动启动
   */
  watch(): void {
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
      console.error('浏览器不支持音频录制功能')
      return
    }

    this.isWatchBrowserVolumeChange = true

    const audioBuffer: Float32Array[] = []

    const MAX_BUFFER_SIZE = 10

    navigator.mediaDevices.getUserMedia({ audio: true })
      .then((stream) => {
        const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
        const analyser = audioContext.createAnalyser()
        const microphone = audioContext.createMediaStreamSource(stream)

        analyser.fftSize = 256
        const bufferLength = analyser.frequencyBinCount
        const dataArray = new Uint8Array(bufferLength)

        const processor = audioContext.createScriptProcessor(4096, 1, 1)

        processor.onaudioprocess = (e) => {
          const data = e.inputBuffer.getChannelData(0).slice()
          audioBuffer.push(data)
          if (audioBuffer.length > MAX_BUFFER_SIZE) {
            audioBuffer.shift()
          }
        }

        microphone.connect(processor)
        microphone.connect(analyser)
        processor.connect(audioContext.destination)

        const checkVolume = (): void => {
          analyser.getByteFrequencyData(dataArray)

          let sum = 0
          for (let i = 0; i < bufferLength; i++) {
            sum += dataArray[i]
          }
          const average = sum / bufferLength

          const volumePercent = (average / 255) * 100

          if (volumePercent > 20 && this.systemStatus === SystemStatus.OFFLINE) {
            this.recordActuatorInstance.triggerHandlerActive()
            this.processorAudioInstance.triggerHandlerActive()
            this.splitAudioInstance.triggerHandlerActive()
            this.latRequestInstance.triggerHandlerActive()
            this.systemStatus = SystemStatus.EXECUTE

            this.recordActuatorInstance.handle(audioBuffer)

            processor.disconnect()
            microphone.disconnect()
            audioContext.close()
            stream.getTracks().forEach(track => track.stop())
          }
          else {
            requestAnimationFrame(checkVolume)
          }
        }

        checkVolume()
      })
      .catch((error) => {
        console.error('获取音频权限失败:', error)
      })
  }

  /**
   * z
   *  进入待机状态，应用开始录制音频数据
   */
  public start(): LatController {
    if (this.systemStatus === SystemStatus.OFFLINE) {
      this.recordActuatorInstance.triggerHandlerActive()
      this.processorAudioInstance.triggerHandlerActive()
      this.splitAudioInstance.triggerHandlerActive()
      this.latRequestInstance.triggerHandlerActive()

      this.systemStatus = SystemStatus.EXECUTE
      this.isWatchBrowserVolumeChange = false

      this.recordActuatorInstance.handle(null)
    }

    return this
  }

  /**
   *  应用停止录制音频数据，但是已经录制的数据依然在处理中
   */
  public end(): LatController {
    if (this.systemStatus === SystemStatus.EXECUTE) {
      this.recordActuatorInstance.isHandleDataAcceptedComplete = true
      this.recordActuatorInstance.handle(null)
    }
    return this
  }

  /**
   * 停止所有处理器，强制停止处理，未处理的音频数据会立即清空
   */
  private finish(): void {
    if (this.systemStatus === SystemStatus.EXECUTE) {
      this.recordActuatorInstance.triggerHandlerFinish()
      this.processorAudioInstance.triggerHandlerFinish()
      this.splitAudioInstance.triggerHandlerFinish()
      this.latRequestInstance.triggerHandlerFinish()
      this.systemStatus = SystemStatus.OFFLINE

      this.emit('appFinish')
    }
  }

  private emit<T>(eventName: PublicCustomEventName, data?: T): void {
    this.$bus.emit(eventName, data)
  }

  public on(eventName: PublicCustomEventName, callback: (data?: any) => void): LatController {
    this.$bus.on(eventName, callback)
    return this
  }
}

export default LatController
